package org.mtmi.ui.controls;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.mtmi.ui.GraphicUtil;
import org.mtmi.ui.M;
import org.mtmi.ui.controls.listeners.ControlTappedListener;
import org.mtmi.ui.controls.listeners.MenuListener;
import org.mtmi.ui.controls.listeners.MenuSelectionListener;
import org.mtmi.ui.controls.listeners.TapEvent;
import org.mtmi.ui.scenes.AnimationManager;
import org.mtmi.ui.scenes.COLOR;
import org.mtmi.ui.scenes.IAnimated;
import org.mtmi.ui.scenes.SceneManager;

/**
 * Class to manage menu.
 * @author jgout
 *
 */
public class Menu implements IVisualControl, ControlTappedListener, IAnimated {

	private static final int WIDTH = 40;
	private static final int HEIGHT = 40;
	
	private boolean isOpen = false;
	private List<MenuEntry> entries;
	private Point location;
	private ArrayList<MenuSelectionListener> lis_selection;
	private ArrayList<MenuListener> lis_menu;
	private List<Control> controls;
	private SceneManager sceneManager;
	private int animStep;
	private int flags;
	
	class MenuEntry {
		public String entryName;
		public final int entryId;
		private boolean enabled;
		public MenuEntry(String entryName, int entryId) {
			this.entryName = entryName;
			this.entryId = entryId;
			enabled = true;
		}
	}
	
	public Menu(SceneManager sceneManager, int _flags ) {
		this.flags = _flags; 
		this.sceneManager = sceneManager;
		entries = new ArrayList<MenuEntry>();
	}
	
	public boolean isSceneEditorControl() { return (flags&M.SCENE_EDITOR_CONTROL)==M.SCENE_EDITOR_CONTROL; }
	public boolean isSceneControl() { return (flags & M.SCENE_EDITOR_CONTROL)==M.SCENE_CONTROL; }
	
	/** @return true if menu entry with this id is defined */
	public boolean hasMenuEntry( int id ) {
		return getMenuEntry( id ) !=null;
	}
	
	/**
	 * Adds a menu entry to this menu.
	 * @param entry the label of the menu entry. Should no be null.
	 * @param id the callback id corresponding to this entry
	 */
	public void addMenuEntry(String entry, int id) {
		MenuEntry me = getMenuEntry( id );
		if( me!= null ) throw new IllegalArgumentException("id: "+id+" already defined in menu '"+me.entryName+"'");
		entries.add(new MenuEntry(entry, id));
	}
	
	/**
	 * Remove an entry from the menu.
	 * @param id the id of the menu entry to remove
	 */
	public void removeMenuEntry( int id ) {
		MenuEntry me = getMenuEntry( id );
		if( me!=null ) entries.remove( me );
	}
	
	private MenuEntry getMenuEntry( int id ) {
		for( MenuEntry me : entries ) {
			if( me.entryId == id ) {
				return me;
			}
		}
		return null;
	}
	
	public void setMenuEntryText( int id, String text ) {
		MenuEntry me = getMenuEntry( id );
		if( me!=null ) me.entryName = text;
	}
	
	public void setMenuEntryEnabled( int id, boolean enabled ) {
		MenuEntry me = getMenuEntry( id );
		if( me!=null ) me.enabled = enabled;
	}
	
	public void open(int x, int y) {
		
		if( lis_menu!=null ) {
			MenuListener lis[] = lis_menu.toArray( new MenuListener[lis_menu.size()]);
			for( MenuListener l : lis ) {
				l.menuOpening( this );
			}
		}
		
		isOpen = true;
		animStep=0;
		location = new Point(x, y);
		int yy = y-(entries.size()/2)*70;
		controls = new ArrayList<Control>(entries.size());
		for (MenuEntry e : entries) {
			controls.add(createMenuEntry(x, yy, e));
			yy += 70;
		}
		sceneManager.getScene().redraw();
	}
	
	private static final String D_MENU_ENTRY_ID="idMenuEntry@Menu"; //$NON-NLS-1$

	private Control createMenuEntry(int x, int y, MenuEntry e) {
		int f = isSceneControl() ? M.SCENE_CONTROL : M.SCENE_EDITOR_CONTROL;
		MenuItemButton b = new MenuItemButton( sceneManager.getScene(), f );
		b.setData( D_MENU_ENTRY_ID, e.entryId );
		b.setText(e.entryName);
		b.setEnabled( e.enabled );
		b.setLocation(x+100, y);
		b.addTapListener(this);
		b.animationStart( true );
		return b;
	}

	public boolean isOpen() {
		return isOpen;
	}

	public void close() {
		boolean have_animation=false;
		boolean quit = sceneManager.getScene().isDisposed();
		boolean animated = AnimationManager.getDefault().isAnimated();
		//- remove all menu entries, except MenuItemButton that have animation
		Control cs[] = controls.toArray( new Control[controls.size()] );
		for ( Control c : cs ) {
			if( !quit && animated && c instanceof MenuItemButton ) {
				((MenuItemButton)c).animationStart( false );
				have_animation = true;
			} else {
				c.dispose();
				controls.remove( c );
			}
		}
		if( !have_animation ){
			isOpen = false;
			if( !quit ) {
				sceneManager.getScene().redraw();
			}
			if( lis_menu!=null ) {
				MenuListener lis[] = lis_menu.toArray( new MenuListener[lis_menu.size()]);
				for( MenuListener l : lis ) {
					l.menuOpening( this );
				}
			}
		} else {
			if( AnimationManager.getDefault().add( this ) ) {
				animStep=1;
			}
		}
	}
	
	@Override
	public boolean animationStep() {
		// this is used for self animation and to check end of menu item animation
		if( animStep >= 100 ) {
			animStep = 0;
		} 
		else if( animStep > 0 ) {
			animStep += 10;
			if( animStep>100 ) animStep = 100;
		}
		boolean menu_item_still_animated = false;
		int ii=0,jj=0;
		if( controls!= null ) {
			Control cs[] = controls.toArray( new Control[controls.size()] );
			for( Control c: cs ) {
				if( c instanceof MenuItemButton ) {
					if( ((MenuItemButton)c).isAnimating() ) {
						menu_item_still_animated = true;
						ii++;
					} else {
						c.dispose();
						controls.remove( c );
						jj++;
					}
				}
			}
		}
		
		boolean still_animated = animStep!=0 || menu_item_still_animated ;
		if( !still_animated ) {
			isOpen = false;
			SceneManager.getDefault().getScene().redraw();
			if( lis_menu!=null ) {
				MenuListener lis[] = lis_menu.toArray( new MenuListener[lis_menu.size()]);
				for( MenuListener l : lis ) {
					l.menuOpening( this );
				}
			}
		}
		return still_animated;
	}
	
	@Override
	public boolean isAnimating() {
		return animStep!=0;
	}

	@Override
	public Point getLocation() {
		return location;
	}

	@Override
	public Point getSize() {
		return null;
	}
	
	@Override
	public Rectangle getBounds(Rectangle r) {
		return GraphicUtil.setRect( r, location.x, location.y, 0, 0 );
	}

	@Override
	public void paint(GC gc) {
		if(isOpen) {
			int alpha = 255;
			if( animStep>0 ) {
				alpha =  255 - (255*(animStep))/100 ;
				gc.setAlpha( alpha );
			}
			gc.setLineWidth(4);
			gc.setForeground( COLOR.grey90 );
			gc.setBackground( COLOR.grey60 );
			gc.setLineCap(SWT.CAP_ROUND);
			gc.setLineJoin(SWT.JOIN_ROUND);
			for (Control c : controls) {
				Point loc = c.getLocation();
				Path p = new Path(gc.getDevice());
				p.moveTo(location.x, location.y);
				p.lineTo(location.x+33, loc.y+c.getSize().y/2);
				p.lineTo(loc.x, loc.y+c.getSize().y/2);
				gc.drawPath(p);
			}
			gc.setAlpha( 255 );
			gc.fillOval(location.x-WIDTH/2, location.y-HEIGHT/2, WIDTH, HEIGHT );
			gc.setAlpha( alpha );
			gc.drawOval(location.x-WIDTH/2, location.y-HEIGHT/2, WIDTH, HEIGHT );
		}
	}

	@Override
	public void controlTappedDown(TapEvent event) {}

	@Override
	public void controlTappedUp(TapEvent event) {
		Button b = (Button) event.control;
		int id = b.getData( D_MENU_ENTRY_ID, -1 );
		if( id >= 0 ) {
			MenuSelectionListener lis[] = lis_selection.toArray( new MenuSelectionListener[lis_menu.size()]);
			for( MenuSelectionListener l : lis ) {
				l.menuEntrySelected( id );
			}
			close();
		}
	}

	public void addMenuSelectionListener( MenuSelectionListener listener ) {
		if( lis_selection==null ) {
			lis_selection = new ArrayList<MenuSelectionListener>(); 
		}
		this.lis_selection.add( listener );
	}
	public void removeMenuSelectionListener( MenuSelectionListener listener ) {
		if( lis_selection != null ) {
			lis_selection.remove( listener );
		}
	}
	public void addMenuListener( MenuListener listener ) {
		if( lis_menu==null ) {
			lis_menu = new ArrayList<MenuListener>(); 
		}
		this.lis_menu.add( listener );
	}
	public void removeMenuListener( MenuListener listener ) {
		if( lis_menu!=null ) {
			lis_menu.remove( listener ); 
		}
	}

	
}
